package io.github.chutian0610.sqlparser.token

import io.github.chutian0610.sqlparser.Location
import java.nio.CharBuffer

class CodePointCharStream private constructor(
    private val charBuffer: CharBuffer,
    private var line: Long,
    private var column: Long
) {
    fun location(): Location {
        return Location(line, column)
    }

    fun peek(): CodePoint? {
        if (charBuffer.remaining() == 0) {
            return null
        }
        charBuffer.mark()
        val c: CodePoint? = readCodePoint()
        charBuffer.reset()
        return c
    }

    private fun readCodePoint(): CodePoint? {
        if (!charBuffer.hasRemaining()) return null
        val firstChar = charBuffer.get()
        if (Character.isHighSurrogate(firstChar) && charBuffer.hasRemaining()) {
            // peek next char to check if it is low surrogate
            val secondChar = charBuffer.get(charBuffer.position())
            if (Character.isLowSurrogate(secondChar)) {
                charBuffer.get()
                return CodePoint.fromInt(Character.toCodePoint(firstChar, secondChar))
            }
        }
        return CodePoint.fromChar(firstChar)
    }

    /**
     * peek next n char.(n start from 1)
     */
    fun peek(index: Int): CodePoint? {
        check(index > 0) { "peek index must > 0" }
        if (charBuffer.remaining() == 0) {
            return null
        }
        charBuffer.mark()
        var c: CodePoint? = null
        var cursor = index
        while (cursor > 0) {
            c = readCodePoint()
            if (c != null) {
                cursor--
            } else {
                c = null
                break
            }
        }
        charBuffer.reset()
        return c
    }

    fun next(): CodePoint? {
        if (charBuffer.remaining() == 0) {
            return null
        }
        // consume the current char
        val c: CodePoint? = readCodePoint()
        if (CodePoint.equalsChar(c, '\n')) {
            line++
            column = 1
        } else {
            column++
        }
        return c
    }

    fun slice(): CodePointCharStream {
        return CodePointCharStream(this.charBuffer.slice(), this.line, this.column)
    }

    companion object {
        fun fromString(str: String): CodePointCharStream {
            return CodePointCharStream(CharBuffer.wrap(str), 1, 1)
        }
    }
}